package com.xbongbong.parent.condition;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.xbongbong.paas.constant.MessageConstant;
import com.xbongbong.paas.domain.entity.ext.ConditionsEntityExt;
import com.xbongbong.paas.enums.ComboTypeEnum;
import com.xbongbong.paas.enums.ConditionEnum;
import com.xbongbong.paas.enums.DateTimeEnum;
import com.xbongbong.paas.enums.FieldTypeEnum;
import com.xbongbong.paas.field.FieldAttrEntity;
import com.xbongbong.paas.field.ItemPoJo;
import com.xbongbong.paas.field.LinkConditionPoJo;
import com.xbongbong.paas.field.LinkInfoPojo;
import com.xbongbong.paas.help.ProFormHelp;
import com.xbongbong.paas.toolbox.exception.XbbException;
import com.xbongbong.paas.toolbox.util.CollectionsUtil;
import com.xbongbong.paas.toolbox.util.PaasDateUtil;
import com.xbongbong.paas.toolbox.util.StringUtil;
import com.xbongbong.paas.util.ExplainUtil;
import com.xbongbong.parent.util.ConditionUtil;
import com.xbongbong.parent.util.LinkDataUtil;
import com.xbongbong.pro.condition.pojo.ConditionDTO;
import com.xbongbong.pro.constant.BusinessConstant;
import com.xbongbong.pro.enums.SaasMarkEnum;
import com.xbongbong.pro.enums.businessrule.enums.RuleValueTypeEnum;
import com.xbongbong.pro.enums.errorcodes.SystemErrorCodeEnum;
import com.xbongbong.pro.user.pojo.ReturnUserAndDepartmentPojo;
import com.xbongbong.saas.constant.StringConstant;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.script.Script;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.rangeQuery;
import static org.elasticsearch.index.query.QueryBuilders.scriptQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
import static org.elasticsearch.index.query.QueryBuilders.termsQuery;

/**
 * @author 吴峰
 * @version v1.0
 * @date 2019/11/4 11:10
 * @since v1.0
 */
@Component
public class ProConditionHelp {

    @Resource
    private ProFormHelp proFormHelp;
    @Resource
    private ConditionFieldHandlerFactory conditionFieldHandlerFactory;

    /**
     * 拼装搜索条件的数据
     * @param conditionDTO
     * @return
     */
    public List<ConditionsEntityExt> getConditionList(ConditionDTO conditionDTO) throws XbbException {
        Map<String, FieldAttrEntity> relyExplainMap = conditionDTO.getRelyExplainMap();
        Map<String, FieldAttrEntity> explainMap = conditionDTO.getExplainMap();
        Map<String, Object> valueMap = conditionDTO.getValueMap();
        List<LinkConditionPoJo> condition = conditionDTO.getCondition();
        List<ConditionsEntityExt> list = conditionDTO.getList();
        String corpid = conditionDTO.getCorpid();
        // 拼接查找条件
        if (CollectionsUtil.isNotEmpty(condition)) {
            for (LinkConditionPoJo item : condition) {
                // 联动字段包含联动条件中设置的字段
                if (relyExplainMap.containsKey(item.getRely())) {
                    ConditionsEntityExt conditionsEntityExt = new ConditionsEntityExt();
                    String relyAttr = item.getRely();

                    // 获取联动条件的比较字段
                    FieldAttrEntity relyFieldAttrEntity = relyExplainMap.get(relyAttr);

                    // 设置条件
                    Object valueObj;
                    Integer relyFieldType = relyFieldAttrEntity.getFieldType();
                    if (Objects.nonNull(item.getValueType()) && Objects.equals(item.getValueType(), RuleValueTypeEnum.FIXED_VALUE.getType())) {
                        // 固定值
                        valueObj = item.getValue();
                        ConditionFieldHandler conditionFieldHandler = conditionFieldHandlerFactory.getRelyFormFixedValueByFieldType(relyFieldType);
                        valueObj = conditionFieldHandler.handlerBusinessDataFixedValue(corpid, relyFieldAttrEntity, conditionDTO, valueObj);
                        dealWithConditionList(list, valueObj, relyFieldType, item);
                    } else {
                        // 根据条件中的字段获取字段属性
                        FieldAttrEntity fieldAttrEntity = getFieldByConditionAttr(item.getAttr(), explainMap);
                        // 校验关联数据或者系统关联业务是否关联同一业务或者同一表单
                        validateTheForm(fieldAttrEntity, relyFieldAttrEntity, conditionDTO.getLinkBusinessType());
                        // 设置条件
                        conditionsEntityExt.setAttr(relyAttr);
                        conditionsEntityExt.setFieldType(relyFieldType);
                        Object equalValue = valueMap.get(item.getAttr());
                        if (isEmpty(equalValue, fieldAttrEntity.getFieldType())) {
                            // 设置为空的数据
                            if (Objects.nonNull(item.getValueType())) {
                                // 加了等于和不等于之后，需要加下下面的逻辑
                                if (Objects.equals(ConditionEnum.NOEQUAL.getSymbol(), item.getSymbol())) {
                                    conditionsEntityExt.setSymbol(ConditionEnum.NOEMPTY.getSymbol());
                                    list.add(conditionsEntityExt);
                                } else {
                                    conditionsEntityExt.setSymbol(ConditionEnum.EMPTY.getSymbol());
                                    list.add(conditionsEntityExt);
                                }
                            } else {
                                // 原逻辑
                                conditionsEntityExt.setSymbol(ConditionEnum.EMPTY.getSymbol());
                                list.add(conditionsEntityExt);
                            }
                        } else {
                            if (validateIsLinkData(fieldAttrEntity.getFieldType(), relyFieldAttrEntity.getFieldType())) {
                                Map<String, Object> attrMap = (Map<String, Object>) valueMap.get(item.getAttr());
                                Map<String, Object> dataMap = (Map<String, Object>) attrMap.get(StringConstant.JSON_DATA);
                                if (dataMap == null) {
                                    continue;
                                }
                                LinkInfoPojo thisLinkInfoPojo = fieldAttrEntity.getLinkInfo();
                                // 获取关联数据的主键，然后去拿对应的数据
                                equalValue = dataMap.get(thisLinkInfoPojo.getLinkKey());
                                valueMap.put(item.getAttr(), equalValue);
                            }
                            if (Objects.nonNull(item.getValueType())) {
                                // 动态值
                                ConditionFieldHandler conditionFieldHandler = conditionFieldHandlerFactory.getHandlerRelyFormByFieldType(relyFieldType);
                                valueObj = conditionFieldHandler.handlerBusinessRelyFormField(corpid, fieldAttrEntity, relyFieldAttrEntity, item, equalValue, conditionDTO.getLinkFormId(), conditionDTO.getLinkBusinessType(), conditionDTO.getLinkSaasMark());
                                dealWithConditionList(list, valueObj, relyFieldType, item);
                            } else {
                                // 原逻辑-设置相等条件
                                conditionEqual(list, corpid, conditionDTO.getLinkFormId(), conditionDTO.getLinkSaasMark(), conditionDTO.getLinkBusinessType(), valueMap, equalValue, relyFieldType, item, conditionsEntityExt, relyFieldAttrEntity, fieldAttrEntity);
                            }
                        }

                    } // 兼容以前逻辑

                }
            }
        }
        return list;
    }

    /**
     * 拼接查询条件
     * @param conditionList
     * @param value
     * @param relyFieldType
     * @param linkCondition
     * @return
     * @author xingxing.xiao
     * @date 2020/07/30 19:44
     */
    private void dealWithConditionList (List<ConditionsEntityExt> conditionList, Object value, Integer relyFieldType, LinkConditionPoJo linkCondition) {
        ConditionsEntityExt conditionsEntityExt = new ConditionsEntityExt(linkCondition.getRely(), "", relyFieldType, linkCondition.getSymbol());
        // 处理值
        if (value instanceof List) {
            List<Object> list = JSON.parseArray(JSON.toJSONString(value), Object.class);
            conditionsEntityExt.setValue(list);
        } else if (Objects.equals(FieldTypeEnum.DATAID.getAlias(), linkCondition.getRely()) && Objects.nonNull(value)) {
            conditionsEntityExt.setValue(Arrays.asList(value));
        } else {
            List<Object> list = new ArrayList<>();
            list.add(value);
            conditionsEntityExt.setValue(list);
        }
        conditionList.add(conditionsEntityExt);
    }

    /**
     * 系统关联业务多条件拼接
     * 注：此处只是把代码提取到方法中，逻辑未做更改
     * @param corpid
     * @param linkFormId
     * @param linkSaasMark
     * @param linkBusinessType
     * @param valueMap
     * @param equalValue
     * @param relyFieldType
     * @param item
     * @param conditionsEntityExt
     * @param list 要拼接的条件
     * @param relyFieldAttrEntity
     * @param fieldAttrEntity
     */
    private void conditionEqual (List<ConditionsEntityExt> list, String corpid, Long linkFormId, Integer linkSaasMark, Integer linkBusinessType,
                                 Map<String, Object> valueMap, Object equalValue, Integer relyFieldType, LinkConditionPoJo item, ConditionsEntityExt conditionsEntityExt, FieldAttrEntity relyFieldAttrEntity, FieldAttrEntity fieldAttrEntity) throws XbbException {

        FieldTypeEnum fieldTypeEnum = FieldTypeEnum.getFieldTypeEnum(relyFieldType);
        switch (fieldTypeEnum) {
            case TEXT:
            case TEXTAREA:
                if (Objects.isNull(valueMap.get(item.getAttr()))) {
                    equalValue = "";
                }
                conditionsEntityExt.setSymbol(ConditionEnum.EQUAL.getSymbol());
                List<Object> textValue = new ArrayList<>();
                textValue.add(equalValue);
                conditionsEntityExt.setValue(textValue);
                list.add(conditionsEntityExt);
                break;
            case USER:
            case DEPT:
                if (Objects.nonNull(valueMap.get(item.getAttr()))) {
                    ReturnUserAndDepartmentPojo returnUserAndDepartmentPojo = JSONObject.parseObject(JSON.toJSONString(equalValue), ReturnUserAndDepartmentPojo.class);
                    if (Objects.nonNull(returnUserAndDepartmentPojo)) {
                        conditionsEntityExt.setSymbol(ConditionEnum.EQUAL.getSymbol());
                        List<Object> value = new ArrayList<>();
                        value.add(returnUserAndDepartmentPojo.getId());
                        conditionsEntityExt.setValue(value);
                        list.add(conditionsEntityExt);
                    }
                }
                break;
            case COMBO:
            case RADIO_GROUP:
                if (Objects.equals(ComboTypeEnum.CUSTOM.getType(), relyFieldAttrEntity.getComboType())) {
                    // 从valueMap里获取text，对比目标表单的text，拿出目标表单的value，之后查出es对应的数据
                    List<ItemPoJo> itemPoJoList = relyFieldAttrEntity.getItems();
                    if (Objects.equals(linkSaasMark, SaasMarkEnum.SAAS.getCode())) {
                        itemPoJoList = proFormHelp.getItem(relyFieldAttrEntity, linkBusinessType, corpid, linkFormId);
                    }
                    Map<Object, ItemPoJo> itemMap = new HashMap<>(itemPoJoList.size());
                    for (ItemPoJo itemPoJo : itemPoJoList) {
                        itemMap.put(itemPoJo.getText(), itemPoJo);
                    }
                    Object text = valueMap.get(item.getAttr());
                    if (Objects.nonNull(itemMap.get(text))) {
                        text = itemMap.get(text).getValue();
                    }
                    conditionsEntityExt.setSymbol(ConditionEnum.EQUAL.getSymbol());
                    List<Object> value = new ArrayList<>();
                    value.add(text);
                    conditionsEntityExt.setValue(value);
                    list.add(conditionsEntityExt);
                } else {
                    Object text = valueMap.get(item.getAttr());
                    conditionsEntityExt.setSymbol(ConditionEnum.EQUAL.getSymbol());
                    List<Object> value = new ArrayList<>();
                    value.add(text);
                    conditionsEntityExt.setValue(value);
                    list.add(conditionsEntityExt);
                }
                break;
            case COMBO_CHECK:
            case CHECKBOX_GROUP:
                List<Object> textList = (ArrayList) valueMap.get(item.getAttr());
                if (Objects.equals(ComboTypeEnum.CUSTOM.getType(), relyFieldAttrEntity.getComboType())) {
                    List<ItemPoJo> itemPoJoList = relyFieldAttrEntity.getItems();
                    List<Object> valueList = new ArrayList<>();
                    for (ItemPoJo itemPoJo : itemPoJoList) {
                        if (textList.contains(itemPoJo.getText())) {
                            valueList.add(itemPoJo.getValue());
                        }
                    }
                    conditionsEntityExt.setSymbol(ConditionEnum.EQUAL.getSymbol());
                    conditionsEntityExt.setValue(valueList);
                    list.add(conditionsEntityExt);

                } else {
                    conditionsEntityExt.setSymbol(ConditionEnum.EQUAL.getSymbol());
                    conditionsEntityExt.setValue(textList);
                    list.add(conditionsEntityExt);
                }
                break;
            case USER_GROUP:
            case DEPT_GROUP:
                if (Objects.nonNull(valueMap.get(item.getAttr()))) {
                    List<ReturnUserAndDepartmentPojo> organizationList = JSON.parseArray(JSON.toJSONString(valueMap.get(item.getAttr())), ReturnUserAndDepartmentPojo.class);
                    if (!organizationList.isEmpty()) {
                        List<Object> organizationIdList = new ArrayList<>();
                        organizationList.forEach(value->{
                            organizationIdList.add(value.getId());
                        });
                        conditionsEntityExt.setSymbol(ConditionEnum.EQUAL.getSymbol());
                        conditionsEntityExt.setValue(organizationIdList);
                        list.add(conditionsEntityExt);
                    }
                }
                break;
            case ADDRESS:
            case LOCATION:
                Map<String, Objects> addressJson = (Map)  valueMap.get(item.getAttr());
                conditionsEntityExt.setSymbol(ConditionEnum.EQUAL.getSymbol());
                List<Object> addressValueList = new ArrayList<>();
                addressValueList.add(addressJson);
                conditionsEntityExt.setValue(addressValueList);
                list.add(conditionsEntityExt);
                break;
            case NUM:
                // 数字、日期 等无需keyword的字段
                conditionsEntityExt.setSymbol(ConditionEnum.EQUAL.getSymbol());
                List<Object> numValueList = new ArrayList<>();
                numValueList.add(valueMap.get(item.getAttr()));
                conditionsEntityExt.setValue(numValueList);
                list.add(conditionsEntityExt);
                break;
            case DATETIME:
                Object value = valueMap.get(item.getAttr());
                    if (Objects.equals(DateTimeEnum.TIME.getType(), fieldAttrEntity.getDateType())
                            && Objects.equals(DateTimeEnum.DATETIME.getType(), relyFieldAttrEntity.getDateType())) {
                        // 如果本表单的日期类型为年月日，目标表单的日期类型为年月日时分；则目标表单搜索为匹配到年月日即可，即是这一天的凌晨到23:59:59
                        Long beforeDate = PaasDateUtil.getBeforeDayBreak(Long.valueOf(String.valueOf(value)));
                        Long endDate = PaasDateUtil.getDayLastSecond(Long.valueOf(String.valueOf(value)));
                        // 数字、日期 等无需keyword的字段
                        conditionsEntityExt.setSymbol(ConditionEnum.RANGE.getSymbol());
                        List<Object> dateList = new ArrayList<>();
                        dateList.add(beforeDate);
                        dateList.add(endDate);
                        conditionsEntityExt.setValue(dateList);
                        list.add(conditionsEntityExt);
                    } else if (Objects.equals(DateTimeEnum.DATETIME.getType(), fieldAttrEntity.getDateType())
                            && Objects.equals(DateTimeEnum.TIME.getType(), relyFieldAttrEntity.getDateType())) {
                        // 如果本表单的日期类型为年月日时分，目标表单的日期类型为年月日；则目标表单搜索为匹配到年月日，即这一天的凌晨00:00:00即可
                        Long beforeDate = PaasDateUtil.getBeforeDayBreak(Long.valueOf(String.valueOf(value)));
                        conditionsEntityExt.setSymbol(ConditionEnum.EQUAL.getSymbol());
                        List<Object> dateList = new ArrayList<>();
                        dateList.add(beforeDate);
                        conditionsEntityExt.setValue(dateList);
                        list.add(conditionsEntityExt);
                    }else if (Objects.equals(DateTimeEnum.DATETIME.getType(), fieldAttrEntity.getDateType())
                            && Objects.equals(DateTimeEnum.DATETIME.getType(), relyFieldAttrEntity.getDateType())) {
                        // 「日期时间」和「日期时间」可以互相对比。因为没有精确到00秒，所以只能检索00秒到59秒。
                        Long beforeDate = PaasDateUtil.getDateFirstSecond(Long.valueOf(String.valueOf(value)));
                        Long endDate = PaasDateUtil.getDateLastSecond(Long.valueOf(String.valueOf(value)));
                        // 数字、日期 等无需keyword的字段
                        conditionsEntityExt.setSymbol(ConditionEnum.RANGE.getSymbol());
                        List<Object> dateList = new ArrayList<>();
                        dateList.add(beforeDate);
                        dateList.add(endDate);
                        conditionsEntityExt.setValue(dateList);
                        list.add(conditionsEntityExt);
                    } else {
                        conditionsEntityExt.setSymbol(ConditionEnum.EQUAL.getSymbol());
                        List<Object> dateList = new ArrayList<>();
                        dateList.add(value);
                        conditionsEntityExt.setValue(dateList);
                        list.add(conditionsEntityExt);
                    }
                break;
            case SERIALNO:
                conditionsEntityExt.setSymbol(ConditionEnum.EQUAL.getSymbol());
                List<Object> serialNoList = new ArrayList<>();
                serialNoList.add(valueMap.get(item.getAttr()));
                conditionsEntityExt.setValue(serialNoList);
                list.add(conditionsEntityExt);
                break;
            case LINK_DATA:
            case LINK_BUSINESS_SINGLE:
            case DATAID:
                Object id = getIdByLinkBusiness(valueMap.get(item.getAttr()));
                if (Objects.nonNull(id)) {
                    conditionsEntityExt.setSymbol(ConditionEnum.EQUAL.getSymbol());
                    List<Object> valueList = new ArrayList<>();
                    valueList.add(id);
                    conditionsEntityExt.setValue(valueList);
                    list.add(conditionsEntityExt);
                }
                break;
            case LINK_DATA_MULTI:
                Object linkDataMultiValue = valueMap.get(item.getAttr());
                Object linkDataMultiId = ProConditionHelp.getLinkDataMultiIdList(fieldAttrEntity, linkDataMultiValue);
                if (Objects.nonNull(linkDataMultiId)) {
                    conditionsEntityExt.setSymbol(ConditionEnum.EQUAL.getSymbol());
                    if (linkDataMultiId instanceof List) {
                        List<Object> valueList = new ArrayList<>((Collection<?>) linkDataMultiId);
                        conditionsEntityExt.setValue(valueList);
                    } else {
                        List<Object> valueList = new ArrayList<>();
                        valueList.add(linkDataMultiId);
                        conditionsEntityExt.setValue(valueList);
                    }
                    list.add(conditionsEntityExt);
                }
                break;
            default:
                conditionsEntityExt.setSymbol(ConditionEnum.EQUAL.getSymbol());
                List<Object> valueList = new ArrayList<>();
                valueList.add(valueMap.get(item.getAttr()));
                conditionsEntityExt.setValue(valueList);
                list.add(conditionsEntityExt);
                break;
        }
    }
    /**
     * 判断是否是同一表单，如果不是，返回空列表
     * @param fieldAttrEntity
     * @param relyFieldAttrEntity
     * @param linkBusinessType
     * @param linkSaasMark
     * @param thisFormId 本表单的表单id
     * @return false：不需要查es true：需要查es
     */
    public static void validateTheForm(FieldAttrEntity fieldAttrEntity, FieldAttrEntity relyFieldAttrEntity, Integer linkBusinessType, Integer linkSaasMark, Long thisFormId, Long targetFormId, Integer businessType) throws XbbException {
        ConditionUtil.validateTheForm(fieldAttrEntity, relyFieldAttrEntity, thisFormId, targetFormId, linkBusinessType, businessType);
    }
    /**
     * 判断是否是同一表单，如果不是，返回空列表
     * @param fieldAttrEntity       当前表的字段
     * @param relyFieldAttrEntity   关联表的字段
     * @param linkBusinessType      关联表的业务
     * @return false：不需要查es true：需要查es
     */
    public void validateTheForm(FieldAttrEntity fieldAttrEntity, FieldAttrEntity relyFieldAttrEntity, Integer linkBusinessType) throws XbbException {
        if ((Objects.equals(fieldAttrEntity.getFieldType(), FieldTypeEnum.LINK_DATA.getType()) || Objects.equals(fieldAttrEntity.getFieldType(), FieldTypeEnum.LINK_DATA_MULTI.getType()))
                && (Objects.equals(relyFieldAttrEntity.getFieldType(), FieldTypeEnum.LINK_DATA.getType()) || Objects.equals(relyFieldAttrEntity.getFieldType(), FieldTypeEnum.LINK_DATA_MULTI.getType()))) {
            LinkInfoPojo thisLinkInfoPojo = fieldAttrEntity.getLinkInfo();
            LinkInfoPojo targetLinkInfoPojo = relyFieldAttrEntity.getLinkInfo();
            // 判断是否是同一表单，不是的话，就返回空列表
            if (!Objects.equals(thisLinkInfoPojo.getLinkBusinessType(), targetLinkInfoPojo.getLinkBusinessType())
                    || !Objects.equals(thisLinkInfoPojo.getLinkFormId(), targetLinkInfoPojo.getLinkFormId())) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_NOT_SAME);
            } else if (Objects.equals(thisLinkInfoPojo.getLinkBusinessType(), linkBusinessType)
                    && !Objects.equals(thisLinkInfoPojo.getLinkFormId(), targetLinkInfoPojo.getLinkFormId())) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_NOT_SAME);
            }
        }else if (Objects.equals(FieldTypeEnum.LINK_BUSINESS_SINGLE.getType(), fieldAttrEntity.getFieldType())) {
            // 如果是关联业务单选
            if (Objects.equals(FieldTypeEnum.DATAID.getType(), relyFieldAttrEntity.getFieldType())) {
                // 当目标表单的条件字段为dataId时，需要判断目标表单是否和当前表单是否为同一业务
                if (!Objects.equals(linkBusinessType, fieldAttrEntity.getLinkedType())) {
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_NOT_SAME);
                }
            } else if (Objects.equals(FieldTypeEnum.LINK_BUSINESS_SINGLE.getType(), relyFieldAttrEntity.getFieldType())) {
                // 当都是关联业务单选时，需要判断是否为同一业务
                if (!Objects.equals(fieldAttrEntity.getLinkedType(), relyFieldAttrEntity.getLinkedType())) {
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_NOT_SAME);
                }
            } else if (Objects.equals(FieldTypeEnum.LINK_DATA.getType(), relyFieldAttrEntity.getFieldType()) || Objects.equals(FieldTypeEnum.LINK_DATA_MULTI.getType(), relyFieldAttrEntity.getFieldType())) {
                // 当都是关联业务单选时，需要判断是否为同一业务
                LinkInfoPojo targetLinkInfoPojo = relyFieldAttrEntity.getLinkInfo();
                if (!Objects.equals(fieldAttrEntity.getLinkedType(), targetLinkInfoPojo.getLinkBusinessType())) {
                    throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_NOT_SAME);
                }
            }
        } else if (Objects.equals(FieldTypeEnum.DATAID.getType(), fieldAttrEntity.getFieldType())) {
            // 当目标表单的条件字段为dataId时，需要判断目标表单是否和当前表单是否为同一业务
            if (Objects.equals(FieldTypeEnum.LINK_BUSINESS_SINGLE.getType(), relyFieldAttrEntity.getFieldType())
                    && !Objects.equals(linkBusinessType, relyFieldAttrEntity.getLinkedType())) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_NOT_SAME);
            }
            if ((Objects.equals(FieldTypeEnum.LINK_DATA.getType(), relyFieldAttrEntity.getFieldType()) || Objects.equals(FieldTypeEnum.LINK_DATA_MULTI.getType(), relyFieldAttrEntity.getFieldType()))
                    && !Objects.equals(relyFieldAttrEntity.getLinkInfo().getLinkBusinessType(), linkBusinessType)) {
                throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.FORM_NOT_SAME);
            }
        }
    }



    /**
     * 根据条件中字段获取字段属性
     * @param attr
     * @param explainMap
     * @return
     */
    public FieldAttrEntity getFieldByConditionAttr(String attr, Map<String, FieldAttrEntity> explainMap) throws XbbException {
        FieldAttrEntity fieldAttrEntity;
        if (attr.contains(StringConstant.POINT)) {
            String[] attrArray = attr.split("\\.");
            FieldAttrEntity subFormField = explainMap.get(attrArray[0]);
            Map<String, FieldAttrEntity> subFormMap = ExplainUtil.getExplainMapByList(subFormField.getSubForm().getItems());
            fieldAttrEntity = subFormMap.get(attrArray[1]);
        } else {
            fieldAttrEntity = explainMap.get(attr);
        }
        if (Objects.isNull(fieldAttrEntity)) {
            throw new XbbException(SystemErrorCodeEnum.API_ERROR_100001, MessageConstant.THIS_FIELD_TYPE_NOT_EXIST);
        }
        return fieldAttrEntity;
    }

    public static Object getLinkDataMultiIdList(FieldAttrEntity fieldAttrEntity, Object value) {
        if (Objects.equals(fieldAttrEntity.getFieldType(), FieldTypeEnum.LINK_BUSINESS_SINGLE.getType())) {
            // 关联业务单选的格式
            JSONArray linkSingleList = JSON.parseArray(JSON.toJSONString(value));
            JSONObject linkSingle = linkSingleList.getJSONObject(0);
            if (Objects.nonNull(linkSingle) && Objects.nonNull(linkSingle.get("id"))) {
                return Arrays.asList(linkSingle.get("id"));
            }
        } else if (Objects.equals(fieldAttrEntity.getFieldType(), FieldTypeEnum.LINK_DATA_MULTI.getType())) {
            // 关联数据多选的格式
            ArrayList<Object> linkDataMultiArray = new ArrayList();
            JSONArray linkSingleList = JSON.parseArray(JSON.toJSONString(value));
            for (int i = 0; i < linkSingleList.size(); i++) {
                JSONObject linkSingle = linkSingleList.getJSONObject(i);
                if (Objects.nonNull(linkSingle) && Objects.nonNull(linkSingle.get(FieldTypeEnum.DATAID.getAlias()))) {
                    linkDataMultiArray.add(linkSingle.get(FieldTypeEnum.DATAID.getAlias()));
                }
            }
            return linkDataMultiArray;
        } else if (Objects.equals(fieldAttrEntity.getFieldType(), FieldTypeEnum.LINK_DATA.getType())) {
            // 关联数据单选的格式
            Map<String, Object> map = (Map) value;
            if (map.containsKey(FieldTypeEnum.DATAID.getAlias())) {
                return Arrays.asList(map.get(FieldTypeEnum.DATAID.getAlias()));
            }
        } else {
            return value;
        }
        return null;
    }

    /**
     * 获取关联业务对应的id或者关联数据对应的id或者主键数据
     * @param value 数据
     */
    public static Object getIdByLinkBusiness(Object value) {
        return LinkDataUtil.getIdByLinkBusiness(value);
    }

    /**
     * 将得到关联业务对应的id或者关联数据对应的id或者主键数据放入es中
     * @param value 数据
     * @param boolQueryBuilder es
     * @param rely
     */
    public void getLinkBusinessSingleData(Object value, BoolQueryBuilder boolQueryBuilder, String rely) {
        Object id = getIdByLinkBusiness(value);
        if (Objects.nonNull(id)) {
            if (Objects.equals(StringConstant.DATA_ID, rely)) {
                boolQueryBuilder.filter(termQuery(rely, id));
            } else {
                boolQueryBuilder.filter(termQuery("data." + rely + ".keyword", id));
            }
        }
    }

    /**
     * 钩子方法：判断当前字段是关联数据，目标字段不是关联数据、数据id、关联业务单选，便于解析主键字段
     * @param fieldType 当前字段的字段类型
     * @param targetFieldType 目标字段的字段类型
     * @return
     */
    public static Boolean validateIsLinkData(Integer fieldType, Integer targetFieldType) {
       return (Objects.equals(fieldType, FieldTypeEnum.LINK_DATA.getType()) || Objects.equals(fieldType, FieldTypeEnum.LINK_DATA_MULTI.getType()))
                && !Objects.equals(FieldTypeEnum.LINK_DATA.getType(), targetFieldType)
                && !Objects.equals(FieldTypeEnum.LINK_DATA_MULTI.getType(), targetFieldType)
                && !Objects.equals(FieldTypeEnum.DATAID.getType(), targetFieldType)
                && !Objects.equals(FieldTypeEnum.LINK_BUSINESS_SINGLE.getType(), targetFieldType);
    }

    /**
     * 判断字段类型为关联数据、系统关联数据，所选数据
     * @param targetFieldType
     * @return
     */
    public Boolean validateLinkData(Integer targetFieldType) {
        return Objects.equals(FieldTypeEnum.LINK_DATA.getType(), targetFieldType)
                || Objects.equals(FieldTypeEnum.LINK_DATA_MULTI.getType(), targetFieldType)
                || Objects.equals(FieldTypeEnum.DATAID.getType(), targetFieldType)
                || Objects.equals(FieldTypeEnum.LINK_BUSINESS_SINGLE.getType(), targetFieldType);
    }
    /**
     * 钩子方法：对数据进行判空
     * @param value
     * @return
     */
    public Boolean isEmpty(Object value, Integer fieldType) {
        boolean isAddress = (Objects.equals(FieldTypeEnum.ADDRESS.getType(), fieldType)
                || Objects.equals(FieldTypeEnum.LOCATION.getType(), fieldType)) && value instanceof Map;
        if (isAddress) {
            JSONObject address = JSONObject.parseObject(JSON.toJSONString(value));
            if (Objects.isNull(address) || StringUtil.isEmpty(address.getString(BusinessConstant.PROVINCE))) {
                return true;
            }
            return false;
        } else {
                boolean isEmpty = value instanceof List && CollectionsUtil.isEmpty(((List)value));
                return Objects.isNull(value) || StringUtil.isEmpty(value.toString()) || isEmpty;
            }
    }

    /**
     * 数据联动需要相等的条件拼装
     * @param value 等于的值
     * @param boolQueryBuilder es
     * @param targetFieldEntity 目标字段
     * @param corpid 公司id
     * @throws XbbException
     */
    public void needEqual(Object value, BoolQueryBuilder boolQueryBuilder,
                               Long formId, Integer saasMark, Integer businessType, FieldAttrEntity targetFieldEntity, FieldAttrEntity fieldAttrEntity, String corpid) throws XbbException {
        String attr = targetFieldEntity.getAttr();
        FieldTypeEnum fieldTypeEnum = FieldTypeEnum.getFieldTypeEnum(targetFieldEntity.getFieldType());
        String data = StringConstant.JSON_DATA + StringConstant.POINT + attr + StringConstant.POINT;
        switch (fieldTypeEnum) {
            case TEXT:
            case TEXTAREA:
                if (value instanceof Map) {
                    Map<String, Object> valueMap = (HashMap) value;
                    value = valueMap.getOrDefault("text", "");
                }
                boolQueryBuilder.filter(termQuery("data." + attr + ".keyword", value));
                break;
            case COMBO:
            case RADIO_GROUP:
                if (value instanceof Map) {
                    Map<String, Object> map = (Map<String, Object>) value;
                    value = map.getOrDefault("text", "");
                }
                Integer comboType = targetFieldEntity.getComboType();
                if (Objects.equals(ComboTypeEnum.CUSTOM.getType(), comboType)) {
                    List<ItemPoJo> itemList = targetFieldEntity.getItems();
                    if (Objects.equals(saasMark, SaasMarkEnum.SAAS.getCode())) {
                        itemList = proFormHelp.getItem(targetFieldEntity, businessType, corpid, formId);
                    }
                    Map<Object, Object> itemMap = new HashMap<>(itemList.size());
                    itemList.forEach(item->{
                        itemMap.put(item.getText(), item.getValue());
                    });
                    if (Objects.nonNull(itemMap.get(value))) {
                        boolQueryBuilder.filter(termQuery("data." + attr + ".keyword", itemMap.get(value)));
                    } else {
                        boolQueryBuilder.filter(termQuery("data." + attr + ".keyword", value));
                    }
                } else {
                    boolQueryBuilder.filter(termQuery("data." + attr + ".keyword", value));
                }
                break;
            case ADDRESS:
            case LOCATION:
                Map<String, Objects> addressJson = (Map) value;
                if (Objects.nonNull(addressJson)) {
                    String keyword = StringConstant.POINT + StringConstant.KEY_WORD;
                    // 先注释掉这个省份，因为有的省份有前面有个异常的字段，暂时用以下几个字符控制
//                            boolQueryBuilder.filter(termQuery(data + BusinessConstant.PROVINCE + keyword, addressJson.get(BusinessConstant.PROVINCE)));
                    boolQueryBuilder.filter(termQuery(data + BusinessConstant.CITY + keyword, addressJson.get(BusinessConstant.CITY)));
                    boolQueryBuilder.filter(termQuery(data + BusinessConstant.DISTRICT + keyword, addressJson.get(BusinessConstant.DISTRICT)));
                    boolQueryBuilder.filter(termQuery(data + BusinessConstant.ADDRESS + keyword, addressJson.get(BusinessConstant.ADDRESS)));
                }
                break;
            case USER:
            case DEPT:
                if (value instanceof String || value instanceof Number) {
                    // 离职的用户或者删除的部门
                    boolQueryBuilder.filter(termQuery("data." + attr + ".keyword", value));
                } else {
                    ReturnUserAndDepartmentPojo returnUserAndDepartmentPojo = JSONObject.parseObject(JSON.toJSONString(value), ReturnUserAndDepartmentPojo.class);
                    if (Objects.nonNull(returnUserAndDepartmentPojo) && Objects.nonNull(returnUserAndDepartmentPojo.getId())) {
                        boolQueryBuilder.filter(termQuery("data." + attr + ".keyword", returnUserAndDepartmentPojo.getId()));
                    }
                }
                break;
            case USER_GROUP:
            case DEPT_GROUP:
                if (Objects.nonNull(value)) {
                    List<ReturnUserAndDepartmentPojo> list = JSON.parseArray(JSON.toJSONString(value), ReturnUserAndDepartmentPojo.class);
                    String key = "data." + attr + ".keyword";
                    BoolQueryBuilder groupBool = boolQuery();
                    if (!list.isEmpty()) {
                        list.forEach(item->{
                            groupBool.should(termsQuery(key, item.getId()));
                        });
                        groupBool.minimumShouldMatch(list.size());
                        boolQueryBuilder.filter(groupBool);
                        Script script = new Script("doc['"+key+"'].values.length == " + list.size());
                        boolQueryBuilder.filter(scriptQuery(script));
                    }
                }
                break;
            case COMBO_CHECK:
            case CHECKBOX_GROUP:
                String name = "data." + attr + ".keyword";
                if (Objects.equals(ComboTypeEnum.CUSTOM.getType(), targetFieldEntity.getComboType())) {
                    List<ItemPoJo> itemList = targetFieldEntity.getItems();
                    Map<Object, Object> itemMap = new HashMap<>(itemList.size());
                    itemList.forEach(item->{
                        itemMap.put(item.getText(), item.getValue());
                    });
                    List<Map<String, Object>> jsonArray = (ArrayList) value;
                    if (Objects.nonNull(jsonArray)) {
                        List<Object> valueList = new ArrayList<>();
                        BoolQueryBuilder comboBool = boolQuery();
                        for (Object object: jsonArray) {
                            if (object instanceof Map) {
                                Map<String, Object> jsonObject = (Map) object;
                                object = jsonObject.get("text");
                            }
                            if (Objects.nonNull(itemMap.get(object))) {
                                valueList.add(itemMap.get(object));
                            }
                        }
                        if (!valueList.isEmpty()) {
                            valueList.forEach(item->{
                                comboBool.should(termsQuery(name, item));
                            });
                        }
                        comboBool.minimumShouldMatch(valueList.size());
                        boolQueryBuilder.filter(comboBool);
                        Script script = new Script("doc['"+name+"'].values.length == " + valueList.size());
                        boolQueryBuilder.filter(scriptQuery(script));
                    }
                } else {
                    List<Map<String, Object>> jsonArray = (ArrayList) value;
                    if (Objects.nonNull(jsonArray)) {
                        List<Object> valueList = new ArrayList<>();
                        BoolQueryBuilder comboBool = boolQuery();
                        for (Object object: jsonArray) {
                            if (object instanceof Map) {
                                Map<String, Object> jsonObject = (Map) object;
                                object = jsonObject.get("value");
                            }
                            valueList.add(object);
                        }
                        if (!valueList.isEmpty()) {
                            valueList.forEach(item->{
                                comboBool.should(termsQuery(name, item));
                            });
                        }
                        comboBool.minimumShouldMatch(valueList.size());
                        boolQueryBuilder.filter(comboBool);
                        Script script = new Script("doc['"+name+"'].values.length == " + valueList.size());
                        boolQueryBuilder.filter(scriptQuery(script));
                    }
                }
                break;
            case NUM:
                // 数字无需keyword的字段
                if (com.xbongbong.paas.toolbox.util.StringUtil.isNotEmpty(String.valueOf(value))) {
                    boolQueryBuilder.filter(termQuery("data." + attr, value));
                }
                break;
            case DATETIME:
                // 日期字段：需要判断本表单字段和目标表单字段的日期类型是否一致，如果不一致，需要进行处理
                // 如果本表单的日期类型为年月日，目标表单的日期类型为年月日时分；则目标表单搜索为匹配到年月日即可，即是这一天的凌晨到23:59:59
                // 如果本表单的日期类型为年月日时分，目标表单的日期类型为年月日；则目标表单搜索为匹配到年月日，即这一天的凌晨00:00:00即可
                if (com.xbongbong.paas.toolbox.util.StringUtil.isNotEmpty(String.valueOf(value))) {
                        if (Objects.equals(DateTimeEnum.TIME.getType(), fieldAttrEntity.getDateType())
                                && Objects.equals(DateTimeEnum.DATETIME.getType(), targetFieldEntity.getDateType())) {
                            // 如果本表单的日期类型为年月日，目标表单的日期类型为年月日时分；则目标表单搜索为匹配到年月日即可，即是这一天的凌晨到23:59:59
                            Long beforeDate = PaasDateUtil.getBeforeDayBreak(Long.valueOf(String.valueOf(value)));
                            Long endDate = PaasDateUtil.getDayLastSecond(Long.valueOf(String.valueOf(value)));
                            boolQueryBuilder.filter(rangeQuery("data." + attr).gte(beforeDate).lt(endDate));
                        } else if (Objects.equals(DateTimeEnum.DATETIME.getType(), fieldAttrEntity.getDateType())
                                && Objects.equals(DateTimeEnum.TIME.getType(), targetFieldEntity.getDateType())) {
                            // 如果本表单的日期类型为年月日时分，目标表单的日期类型为年月日；则目标表单搜索为匹配到年月日，即这一天的凌晨00:00:00即可
                            Long beforeDate = PaasDateUtil.getBeforeDayBreak(Long.valueOf(String.valueOf(value)));
                            boolQueryBuilder.filter(termQuery("data." + attr, beforeDate));
                        }else if (Objects.equals(fieldAttrEntity.getDateType(), DateTimeEnum.DATETIME.getType())
                                && Objects.equals(targetFieldEntity.getDateType(), DateTimeEnum.DATETIME.getType())) {
                            // 「日期时间」和「日期时间」可以互相对比。因为没有精确到00秒，所以只能检索00秒到59秒。
                            Long beforeDate = PaasDateUtil.getDateFirstSecond(Long.valueOf(String.valueOf(value)));
                            Long endDate = PaasDateUtil.getDateLastSecond(Long.valueOf(String.valueOf(value)));
                            boolQueryBuilder.filter(rangeQuery("data." + attr).gte(beforeDate).lt(endDate));
                        } else {
                            boolQueryBuilder.filter(termQuery("data." + attr, value));
                        }
                }
                break;
            case SERIALNO:
                if (value instanceof Map) {
                    Map<String, Object> valueMap = (HashMap) value;
                    value = valueMap.getOrDefault("text", "");
                }
                boolQueryBuilder.filter(termQuery(attr + ".keyword", value));
                break;
            case LINK_DATA:
            case LINK_BUSINESS_SINGLE:
            case DATAID:
                // 关联数据和关联业务单选
                getLinkBusinessSingleData(value, boolQueryBuilder, attr);
                break;
            case LINK_DATA_MULTI:
                if (Objects.nonNull(value)) {
                    Object linkDataMultiId = ProConditionHelp.getLinkDataMultiIdList(fieldAttrEntity, value);
                    if (Objects.nonNull(linkDataMultiId)) {
                        List<Object> valueList = new ArrayList<>();
                        if (linkDataMultiId instanceof List) {
                             valueList = new ArrayList<>((Collection<?>) linkDataMultiId);
                        } else {
                            valueList.add(linkDataMultiId);
                        }
                        String key = "data." + attr + ".keyword";
                        BoolQueryBuilder groupBool = boolQuery();
                        if (CollectionsUtil.isNotEmpty(valueList)) {
                            valueList.forEach(item-> groupBool.should(termsQuery(key, item)));
                            groupBool.minimumShouldMatch(valueList.size());
                            boolQueryBuilder.filter(groupBool);
                            Script script = new Script("doc['"+key+"'].values.length == " + valueList.size());
                            boolQueryBuilder.filter(scriptQuery(script));
                        }
                    }
                }
                break;
            case STAGE_THRUSTER:
                if (value instanceof Map) {
                    Map<String, Object> map = (Map<String, Object>) value;
                    value = map.getOrDefault("text", "");
                }
                List<ItemPoJo> itemList = proFormHelp.getEnableStageComboItems(saasMark, businessType, corpid, formId);
                Map<Object, Object> itemMap = new HashMap<>(itemList.size());
                itemList.forEach(item->{
                    itemMap.put(item.getText(), item.getValue());
                });
                if (Objects.nonNull(itemMap.get(value))) {
                    boolQueryBuilder.filter(termQuery("data." + attr + ".keyword", itemMap.get(value)));
                } else {
                    boolQueryBuilder.filter(termQuery("data." + attr + ".keyword", value));
                }
                break;
            default:
                boolQueryBuilder.filter(termQuery("data." + attr + ".keyword", value));
                break;
        }
    }

    public FieldAttrEntity getDataIdFieldAttr() {
        // 增加一个id字段
        FieldAttrEntity dataIdFieldAttrEntity = new FieldAttrEntity();
        dataIdFieldAttrEntity.setAttr(FieldTypeEnum.DATAID.getAlias());
        dataIdFieldAttrEntity.setAttrName(FieldTypeEnum.DATAID.getAlias());
        dataIdFieldAttrEntity.setFieldType(FieldTypeEnum.DATAID.getType());
        return dataIdFieldAttrEntity;
    }
}
